#include <Kharlia/Engine/Common.h>
#include <Kharlia/Engine/Actor.h>
#include <Kharlia/Engine/World.h>
#include <Kharlia/Engine/Level.h>
#include <Kharlia/Engine/Engine.h>
#include <Kharlia/Engine/Client.h>
#include <Kharlia/Engine/Net.h>
#include <Kharlia/Engine/Player.h>
#include <Kharlia/Engine/Init.h>
#include <Kharlia/Core/App.h>
#include <Kharlia/Core/ObjectUtil.h>

#define KH_ADD_PROPERTY_COPY_CONST_REF(name, getter, setter) \
    add_property(name, make_function(getter, return_value_policy<copy_const_reference>()), setter)

namespace kharlia { namespace engine {

//object AActor::msSimulatedNoOpFunc;
//object AActor::msSimulatedNoOpMeth;
//object AActor::msSimulatedStr;
//getattrofunc AActor::msBaseGetAttr = NULL;
//setattrofunc AActor::msBaseSetAttr = NULL;

//KH_BEGIN_CLASSINFO_NO_BASE(AActor)
//    KH_DEF_FIELD(mPosition, FT_VECTOR3F)
//    KH_DEF_FIELD(mDirection, FT_ENUM)
//    KH_DEF_FIELD(mVelocity, FT_VECTOR3F)
//    KH_DEF_FIELD(mAcceleration, FT_VECTOR3F)
//    KH_DEF_FIELD(mHidden, FT_BOOL)
//    KH_DEF_FIELD(mRole, FT_ENUM) // special cased
//    KH_DEF_FIELD(mRemoteRole, FT_ENUM) // special cased
//    KH_DEF_FIELD(mCollideActors, FT_BOOL)
//    KH_DEF_FIELD(mCollideTiles, FT_BOOL)
//    KH_DEF_FIELD(mCollisionLayer, FT_INT)
//    KH_DEF_FIELD(mBounds, FT_RECTF)
//KH_END_CLASSINFO()
//
//KH_BEGIN_CLASSINFO(ACamera, AActor)
//    KH_DEF_FIELD(mTarget, FT_SHARED_PTR_ACTOR)
//    KH_DEF_FIELD(mTargetOffset, FT_VECTOR3F)
//KH_END_CLASSINFO()
//
//KH_BEGIN_CLASSINFO(APawn, AActor)
//    KH_DEF_FIELD(mMaxVelocity, FT_VECTOR3F)
//    KH_DEF_FIELD(mMaxAcceleration, FT_VECTOR3F)
//    KH_DEF_FIELD(mController, FT_SHARED_PTR_ACTOR)
//KH_END_CLASSINFO()
//
//KH_BEGIN_CLASSINFO(AInfo, AActor)
//KH_END_CLASSINFO()
//
//KH_BEGIN_CLASSINFO(ALevelInfo, AInfo)
//    KH_DEF_FIELD(mBegunPlay, FT_BOOL)
//    KH_DEF_FIELD(mTime, FT_FLOAT)
//    KH_DEF_FIELD(mTimeDilation, FT_FLOAT)
//KH_END_CLASSINFO()

////////////////
// NEW COMPONENT STUFF
//////////////////

KH_BEGIN_CLASSINFO_NO_BASE(ATransform)
    KH_DEF_FIELD(mPosition, FT_VECTOR3F)
KH_END_CLASSINFO()

KH_BEGIN_CLASSINFO_NO_BASE(AMovement)
    KH_DEF_FIELD(mVelocity, FT_VECTOR3F)
    KH_DEF_FIELD(mAcceleration, FT_VECTOR3F)
    KH_DEF_FIELD(mMaxVelocity, FT_VECTOR3F)
    KH_DEF_FIELD(mMaxAcceleration, FT_VECTOR3F)
KH_END_CLASSINFO()

KH_BEGIN_CLASSINFO_NO_BASE(ARender)
    KH_DEF_FIELD(mRenderType, FT_ENUM)
    KH_DEF_FIELD(mDirection, FT_ENUM)
    KH_DEF_FIELD(mHidden, FT_BOOL)
KH_END_CLASSINFO()

KH_BEGIN_CLASSINFO_NO_BASE(APhysics)
    KH_DEF_FIELD(mCollideTiles, FT_BOOL)
    KH_DEF_FIELD(mCollideActors, FT_BOOL)
    KH_DEF_FIELD(mBlockActors, FT_BOOL)
    KH_DEF_FIELD(mCollisionLayer, FT_INT)
    KH_DEF_FIELD(mBounds, FT_RECTF)
KH_END_CLASSINFO()

KH_BEGIN_CLASSINFO_NO_BASE(ACamera)
    KH_DEF_FIELD(mTarget, FT_SHARED_PTR_ACTOR)
    KH_DEF_FIELD(mTargetOffset, FT_VECTOR3F)
KH_END_CLASSINFO()

// -------------------------------------------------
// AComponent
// -------------------------------------------------

AComponent::AComponent(PyObject* self):
    CObject(self),
    mNoDelete(false),
    mPriority(0),
    mNetDirty(false),
    mNetInitial(true),
    mNetOwner(false)
{
}


void AComponent::_OnInit(shared_ptr<CActor> owner) {
    assert(!GetInited());

    mOwner = owner;
    assert(mOwner);
    mLevel = owner->GetLevel();

    StartMsgProc();
}

void AComponent::_OnExit() {
    assert(GetInited());
    if (mNoDelete && mLevel->GetWorld()->GetGameStarted())
        throw RuntimeError("attempted to delete no-delete component during game");

    StopMsgProc();

    mLevel.reset();
    mOwner.reset();
}

void AComponent::Tick(float deltaTime) {
    assert(GetInited());
}

void AComponent::ResetLevel() {
    if (mOwner)
        mLevel = mOwner->GetLevel();
    else
        mLevel.reset();
}

void AComponent::_OnRecvGameStarted(shared_ptr<CGame> game) {
}

void AComponent::_OnRecvGameStopped() {
}

KH_BEGIN_GC(AComponent)
    KH_GC_SHARED_PTR(thisx->mOwner)
    KH_GC_SHARED_PTR(thisx->mLevel)
KH_END_GC()

//object AComponent::CreateEventTasklet(const object& name, uint8 group, const object& args) {
//    object meth = GetSelfAttr(name);
//    object tasklet = mLevel->GetActorTasker()->CreateTasklet(meth);
//    handle<> retval(PyObject_Call(tasklet.ptr(), args.ptr(), NULL));
//    mLevel->GetActorTasker()->InsertTasklet(tasklet, group);
//    return tasklet;
//}

// -------------------------------------------------
// ATransform
// -------------------------------------------------

void ATransform::Move(const FVector3F& delta) {
    if (!GetLevel())
        throw RuntimeError("no level, not initialized");
    GetLevel()->MoveActor(GetOwner(), delta);
}

// -------------------------------------------------
// AMovement
// -------------------------------------------------

void AMovement::_OnInit(shared_ptr<CActor> owner) {
    AComponent::_OnInit(owner);
    mTransform = owner->GetTransform();
    EnsureComponent(mTransform);
    SetPriority(1);
}

void AMovement::_OnExit() {
    mTransform.reset();
    AComponent::_OnExit();
}

void AMovement::Tick(float deltaTime) {
    AComponent::Tick(deltaTime);
    if (!mVelocity.IsZero() && mTransform) {
        //mTransform->AddPosition(mVelocity * deltaTime);
        mTransform->Move(mVelocity * deltaTime);
    }
}

// -------------------------------------------------
// ARender
// -------------------------------------------------

void ARender::_OnInit(shared_ptr<CActor> owner) {
    AComponent::_OnInit(owner);
    mTransform = owner->GetTransform();
    EnsureComponent(mTransform);
    SetPriority(1);
    bool r = GetLevel()->AddRenderable(GetSelfPtr<ARender>());
    assert(r);
}

void ARender::_OnExit() {
    bool r = GetLevel()->RemoveRenderable(GetSelfPtr<ARender>());
    assert(r);
    mTransform.reset();
    AComponent::_OnExit();
}

void ARender::Draw(CClient& client) {
    switch (GetRenderType()) {
    case ART_NONE:
    case ART_IMAGE:
        {
            client.RniDrawSprite(
                GetTransform()->GetPosition(),
                client.TempActorTexture,
                client.TempActorTexture->getOriginalDataSize(),
                ui::colour(1.0f, 1.0f, 1.0f, 1.0f)
            );
        }
        break;
    case ART_ANI:
        break;
    default:
        throw ValueError("Invalid RenderType");
    }
}

// -------------------------------------------------
// APhysics
// -------------------------------------------------

APhysics::APhysics(PyObject* self):
    AComponent(self),
    mCollideTiles(false),
    mCollideActors(false),
    mBlockActors(false),
    mCollisionLayer(0),
    mBounds(),
    mLastActorHitTime(0),
    mLastActorHit(NULL) {}

void APhysics::_OnInit(shared_ptr<CActor> owner) {
    AComponent::_OnInit(owner);
    mTransform = GetOwner()->GetTransform();
    EnsureComponent(mTransform);
    SetPriority(1);
    GetLevel()->AddCollidable(GetSelfPtr<APhysics>());
}

void APhysics::_OnExit() {
    GetLevel()->RemoveCollidable(GetSelfPtr<APhysics>());
    mTransform.reset();
    AComponent::_OnExit();
}

void APhysics::_OnTouched(shared_ptr<APhysics> other) {
}

// -------------------------------------------------
// ACamera
// -------------------------------------------------

void ACamera::_OnInit(shared_ptr<CActor> owner) {
    AComponent::_OnInit(owner);
    mTarget = GetOwner()->GetTransform();
    SetPriority(2);
}

void ACamera::_OnExit() {
    mTarget.reset();
    AComponent::_OnExit();
}

KH_BEGIN_GC(ACamera)
    KH_GC_SHARED_PTR(thisx->mTarget)
KH_END_GC()

// -------------------------------------------------
// AController
// -------------------------------------------------

AController::AController(PyObject* self):
    AComponent(self)
{
}

void AController::_OnInit(shared_ptr<CActor> owner) {
    if (typeid(*this) == typeid(AController))
        throw RuntimeError("AController is abstract");
    AComponent::_OnInit(owner);
    SetPriority(10);
}

void AController::_OnExit() {
    AComponent::_OnExit();
}

void AController::Tick(float deltaTime) {
    AComponent::Tick(deltaTime);
}

KH_BEGIN_GC(AController)
    KH_GC_SHARED_PTR(thisx->mPawn)
KH_END_GC()

// -------------------------------------------------
// APlayerController
// -------------------------------------------------

APlayerController::APlayerController(PyObject* self):
    AController(self)
{
}

void APlayerController::_OnInit(shared_ptr<CActor> owner) {
    AController::_OnInit(owner);
}

void APlayerController::_OnExit() {
    AComponent::_OnExit();
}

void APlayerController::Tick(float deltaTime) {
    AController::Tick(deltaTime);
}

KH_BEGIN_GC(APlayerController)
    KH_GC_SHARED_PTR(thisx->mPlayer)
KH_END_GC()

// -------------------------------------------------
// APawn
// -------------------------------------------------

APawn::APawn(PyObject* self):
    AComponent(self)
{
}

void APawn::_OnInit(shared_ptr<CActor> owner) {
    AComponent::_OnInit(owner);
    SetPriority(5);
}

void APawn::_OnExit() {
    AComponent::_OnExit();
}

void APawn::Tick(float deltaTime) {
    AComponent::Tick(deltaTime);
}

KH_BEGIN_GC(APawn)
    KH_GC_SHARED_PTR(thisx->mController)
KH_END_GC()

// -------------------------------------------------
// ANetwork
// -------------------------------------------------

ANetwork::ANetwork(PyObject* self):
        AComponent(self),
        mHasNativeRep(false),
        mUseNativeRep(false),
        mHasPythonRep(false),
        mUsePythonRep(false),
        mInitial(true),
        mRole(ROLE_AUTHORITY),
        mRemoteRole(ROLE_NONE),
        mRoleInfo(NULL),
        mRemoteRoleInfo(NULL) {

}

ANetwork::~ANetwork() {}

void ANetwork::_OnInit(shared_ptr<CActor> owner) {
    AComponent::_OnInit(owner);

    //InitNativeRep();
    //InitPythonRep();
}

void ANetwork::_OnExit() {
    if (mHasPythonRep)
        ExitPythonRep();
    if (mHasNativeRep)
        ExitNativeRep();
    AComponent::_OnExit();
}

void ANetwork::InitNativeRep() {
}

void ANetwork::ExitNativeRep() {
}


// -------------------------------------------------
// CActor
// -------------------------------------------------

handle<PyTypeObject> CActor::msComponentSlotTypes[SLOT_LAST];

CActor::CActor(PyObject* self):
    CObject(self),
    mID(0),
    mComponents() {}

CActor::~CActor() {}

void CActor::SetOwner(const shared_ptr<CActor>& other) {
    if (mOwner) {
        //mOwner->OnLostChild(GetSelfPtr<CActor>());
    }
    mOwner = other;
    if (mOwner) {
        //mOwner->OnGainedChild(GetSelfPtr<CActor>());
    }
}

void CActor::Tick(float deltaTime) {
    for (uint i = 0; i < mComponents.size(); i++) {
        mComponents[i].Component->Tick(deltaTime);
    }
}

shared_ptr<AComponent> CActor::GetComponentExact(object cls) const {
    if (!PyType_Check(cls.ptr()))
        throw TypeError("cls must be instance of type");
    for (uint i = 0; i < mComponents.size(); i++) {
        const FComponentEntry& c = mComponents[i];
        if (c.Type.get() == (PyTypeObject*)cls.ptr()) {
            return c.Component;
        }
    }
    return shared_ptr<AComponent>();
}

shared_ptr<AComponent> CActor::GetComponent(object cls) const {
    if (!PyType_Check(cls.ptr()))
        throw TypeError("cls must be instance of type");
    for (uint i = 0; i < mComponents.size(); i++) {
        const FComponentEntry& c = mComponents[i];
        if (PyObject_IsInstance(c.Component->GetSelf().ptr(), cls.ptr())) {
            return c.Component;
        }
    }
    return shared_ptr<AComponent>();
}

shared_ptr<AComponent> CActor::GetComponentExact(py::type_info t) const {
    for (uint i = 0; i < mComponents.size(); i++) {
        const FComponentEntry& c = mComponents[i];
        if (t == typeid(*c.Component.get())) {
            return c.Component;
        }
    }
    return shared_ptr<AComponent>();
}

void CActor::AddComponent(const shared_ptr<AComponent>& comp) {
    assert(comp);
    if (comp->GetOwner()) {
        throw RuntimeError("component already has an owner");
    }

    FComponentEntry entry(comp);
    int slotIndex = -1;

    // If there is no owner it shouldn't already be in the list
    assert(std::find(mComponents.begin(), mComponents.end(), entry) == mComponents.end());

    // Find a slot for the component
    for (uint i = 0; i < SLOT_LAST; i++) {
        if (PyType_IsSubtype(comp->GetTypeHandle().get(),
                             msComponentSlotTypes[i].get())) {
            if (mComponentSlots[i])
                throw RuntimeError("slot already filled");
            slotIndex = i;
            break;
        }
    }

    // An exception thrown in OnInit should not leave the actor
    // in a bad state
    shared_ptr<CActor> self = GetSelfPtr<CActor>();
    comp->OnInit(self);
    assert(comp->GetOwner() == self);

    // Insert into slot
    if (slotIndex != -1)
        mComponentSlots[slotIndex] = comp;
    // Insert into vector
    if (mComponents.size() == 0)
        mComponents.push_back(entry);
    else {
        // Keep the vector sorted by priority
        sint16 priority = comp->GetPriority();
        for (uint i = mComponents.size() - 1; i >= 0; i--) {
            if (mComponents[i].Component->GetPriority() <= priority) {
                mComponents.insert(mComponents.begin() + i + 1, entry);
                break;
            }
        }
    }
}

void CActor::RemoveComponent(const shared_ptr<AComponent>& comp) {
    assert(comp);
    if (comp->GetOwner().get() != this)
        return;
    assert(comp->GetInited());
    comp->OnExit();

    for (uint i = 0; i < SLOT_LAST; i++) {
        if (mComponentSlots[i] == comp) {
            mComponentSlots[i].reset();
            break;
        }
    }

    FComponentEntry entry(comp);
    ComponentsType::iterator it = std::find(mComponents.begin(), mComponents.end(), entry);
    if (it == mComponents.end())
        throw RuntimeError("owned component not found in list");
    mComponents.erase(it);
}

void CActor::SetLevel(const shared_ptr<CLevel>& level) {
    mLevel = level;
    for (uint i = 0; i < mComponents.size(); i++) {
        mComponents[i].Component->ResetLevel();
    }
}

void CActor::ClearComponents() {
    for (uint i = 0; i < mComponents.size(); i++) {
        mComponents[i].Component->OnExit();
    }
    for (uint i = 0; i < SLOT_LAST; i++) {
        mComponentSlots[i].reset();
    }
    mComponents.clear();
}

void CActor::OnInit(uint32 id, const shared_ptr<CLevel>& level,
                               const shared_ptr<CActor>& owner) {
    if (mID)
        throw RuntimeError("already initialized");
    mID = id;
    SetLevel(level);
    SetOwner(owner);
}

void CActor::OnExit() {
    ClearComponents();

    //RemoveActor(actor);

    for (int i = mChildren.size() - 1; i > -1; i--) {
        mChildren[i]->SetOwner(mOwner);
    }

    SetOwner(shared_ptr<CActor>());
    SetLevel(shared_ptr<CLevel>());
    mID = 0;
}

void CActor::_OnCreated() {
}

void CActor::_OnDestroyed() {
}

void CActor::_OnGameStarted(shared_ptr<CGame> game) {
    for (uint i = 0; i < mComponents.size(); i++) {
        mComponents[i].Component->SendGameStarted(game);
    }
}

void CActor::_OnGameStopped() {
    for (uint i = 0; i < mComponents.size(); i++) {
        mComponents[i].Component->SendGameStopped();
    }
}

KH_BEGIN_GC(CActor)
    KH_GC_SHARED_PTR(thisx->mLevel)
    KH_GC_SHARED_PTR(thisx->mOwner)
    for (uint i = 0; i < thisx->mChildren.size(); i++) {
        KH_GC_SHARED_PTR(thisx->mChildren[i])
    }
    for (uint i = 0; i < thisx->mComponents.size(); i++) {
        KH_GC_SHARED_PTR(thisx->mComponents[i].Component)
        KH_GC_HANDLE_EX(thisx->mComponents[i].Type, PyTypeObject)
    }
    for (uint i = 0; i < SLOT_LAST; i++) {
        KH_GC_SHARED_PTR(thisx->mComponentSlots[i]);
    }
KH_END_GC()

tuple PSCActor::getstate(CActor& actor) {
    // Get instance dict
    object idict;
    objects::instance<>* inst = downcast<objects::instance<>>(actor.mSelf);
    if (inst->dict)
        idict = object(handle<>(borrowed(inst->dict)));

    // Get components
    list components;
    for (uint i = 0; i < actor.mComponents.size(); i++) {
        components.append(actor.mComponents[i].Component);
    }
    list slots;
    for (uint i = 0; i < SLOT_LAST; i++) {
        slots.append(actor.mComponentSlots[i]);
    }
    return py::make_tuple(idict, components, slots, actor.GetOwner(),
                          actor.GetTag(), actor.GetLevel(), actor.GetID());
}

void PSCActor::setstate(CActor& actor, tuple state) {
    object idict             = state[0];
    list components          = extract<list>(state[1]);
    list slots               = extract<list>(state[2]);
    shared_ptr<CActor> owner = extract<shared_ptr<CActor>>(state[3]);
    unicode tag              = extract<unicode>(state[4]);
    shared_ptr<CLevel> level = extract<shared_ptr<CLevel>>(state[5]);
    uint32 id                = extract<uint32>(state[6]);

    actor.mID = id;
    actor.mTag = tag;
    actor.mOwner = owner;
    actor.mLevel = level;

    if (!idict.is_none()) {
        assert(PyDict_Check(idict.ptr()));
        objects::instance<>* inst = downcast<objects::instance<>>(actor.mSelf);
        if (inst->dict)
            PyDict_Update(inst->dict, idict.ptr());
        else
            inst->dict = incref(idict.ptr());
    }

    if (len(components) > 0) {
        object component, next = GetIterNextFunc(components);
        while (Iterate(next, component)) {
            //actor.AddComponent(extract<shared_ptr<AComponent>>(component));
            shared_ptr<AComponent> compptr = extract<shared_ptr<AComponent>>(component);
            actor.mComponents.push_back(compptr);
        }
    }

    assert(len(slots) == SLOT_LAST);
    for (uint i = 0; i < SLOT_LAST; i++) {
        actor.mComponentSlots[i] = extract<shared_ptr<AComponent>>(slots[i]);
    }
}

/////////////////
// END NEW COMPONENT STUFF
//////////////////

/*

AActor::AActor(PyObject* self):
    CObject(self),
    FDrawable(),
    mName(),
    mOwner(),
    mLevel(),
    mStatic(false),
    mNoDelete(false),
    mDeleteMe(false),
    mPosition(),
    mDirection(DIR_SOUTH),
    mRenderType(ART_NONE),
    mHasMoved(false),
    mHidden(false),
    mNetInitial(true),
    mNetDirty(false),
    mNetSkipActorReplication(false),
    mRole(ROLE_NONE),
    mRemoteRole(ROLE_NONE),
    mBounds(0, 0, 0, 0),
    mCollideActors(false),
    mCollideTiles(false),
    mCollisionLayer(0),
    mLastActorHit(NULL),
    mLastActorHitTime(0.0)
{
    ResetTileGroupNumber();
}

AActor::~AActor() {
    if (!mLevel || !mLevel->GetWorld() || !mLevel->GetWorld()->GetNetIsEnabled())
        replicaManager = NULL;
    // Replica is dereferenced next in ~Replica3 if replicaManager is not NULL
    // So if the engine doesn't exist or the network interface has been shutdown
    // we cannot allow reference to replicaManager
}

void AActor::SetCollideActors(bool value) {
    mCollideActors = value;
    mNetDirty = true;
    //if (mCollideActors)
    //    mLevel->AddCollidable(GetSelfPtr<AActor>());
    //else
    //    mLevel->RemoveCollidable(GetSelfPtr<AActor>());
}

void AActor::SetCollisionLayer(int layer) {
    if (mLevel->CheckLayerExists(layer)) {
        mCollisionLayer = layer;
        mNetDirty = true;
    }
}

void AActor::Tick(float deltaTime) {
    if (!mStatic) {
        if (!mAcceleration.IsZero()) {
            mVelocity += mAcceleration * deltaTime;
            mNetDirty = true;
        }
        if (!mVelocity.IsZero()) {
            Move(mVelocity * deltaTime);
        }
    }
}

void AActor::Move(const FVector3F& delta) {
    //mLevel->MoveActor(GetSelfPtr<AActor>(), delta);
}

void AActor::Attach(shared_ptr<AActor> actor) {
    shared_ptr<AActor>& actorat = actor->mAttachedTo;
    if (actorat) {
        if (actorat.get() == this)
            return;
        else {
            actorat->Detach(actor);
            assert(!actorat);
        }
    }
    actorat = GetSelfPtr<AActor>();
    assert(std::find(mAttached.begin(), mAttached.end(), actor) == mAttached.end());
    mAttached.push_back(actor);
    //object meth = GetSelfAttr("OnAttached");
    //core::GApp->CreateTasklet(GetSelfAttr("OnAttached"))(actor);
    //core::GApp->CreateTasklet(actor->GetSelfAttr("OnAttachedTo"))(actorat);
    // INCOMPLETE
}

void AActor::Detach(shared_ptr<AActor> actor) {
    shared_ptr<AActor>& actorat = actor->mAttachedTo;
    if (actorat.get() != this)
        return;
    actorat.reset();
    AttachedType::iterator it;
    it = std::find(mAttached.begin(), mAttached.end(), actor);
    assert(it != mAttached.end());
    mAttached.erase(it);
    // INCOMPLETE
}

float AActor::Sleep(float time, uint8 group) {
    if (!mLevel)
        throw RuntimeError("no level");
    return mLevel->GetActorTasker()->Sleep(time, group);
}

float AActor::Yield(uint8 group) {
    if (!mLevel)
        throw RuntimeError("no level");
    return mLevel->GetActorTasker()->Yield(group);
}

void AActor::_OnCreated() {
}

void AActor::_OnDestroyed() {
}

void AActor::_OnTouched(shared_ptr<AActor> other) {
    mLevel->GetLogger()->Debug(unicode("Touched by: ") + unicode(other)); 
}

void AActor::CreateReplicator() {
    assert(!mReplicator);
    mReplicator = ConstructObjectEx<CActorReplicator>("kharlia.engine.CActorReplicator");
    mReplicator->BindActor(GetSelfPtr<AActor>());
    assert(mReplicator->GetActor().get() == this);
}

void AActor::DestroyReplicator() {
    if (mReplicator) {
        mReplicator->UnbindActor();
        assert(mReplicator->GetActor().get() == NULL);
        mReplicator.reset();
    }
}

void AActor::SetRemoteRole(ENetRole role) {
    mRemoteRole = role;
    //// We only need to bother tracking an actor if it has a remote role
    //if (role == ROLE_NONE) {
    //    const CLevel::ActorPtrsType& actors = mLevel->GetActorPtrs();
    //    for (uint i = 0; i < actors.size(); i++) {
    //        if (actors[i]->GetOwner().get() == this) {
    //            const_cast<AActor*>(actors[i])->SetRemoteRole(ROLE_NONE);
    //        }
    //    }
    //    if (mLevel->GetWorld()->GetNetIsServer())
    //        BroadcastDestruction();
    //    NetDereference();
    //}
    //else {
    //    if (mOwner && mOwner->GetRemoteRole() < role)
    //        mOwner->SetRemoteRole(role);
    //    NetReference();
    //}
}

void AActor::NetReference() {
    if (mLevel && mLevel->GetWorld())
        mLevel->GetWorld()->NetReference(this);
}

void AActor::NetDereference() {
    if (mLevel && mLevel->GetWorld())
        mLevel->GetWorld()->NetDereference(this);
}

void AActor::Draw(CEngine& engine) {
    switch (GetRenderType()) {
    case ART_NONE:
    case ART_IMAGE:
        {
            engine.RniDrawSprite(
                GetPosition(),
                engine.TempActorTexture,
                engine.TempActorTexture->getOriginalDataSize(),
                ui::colour(1.0f, 1.0f, 1.0f, 1.0f)
            );
        }
        break;
    case ART_ANI:
        break;
    default:
        throw ValueError("Invalid RenderType");
    }
}

object AActor::CreateEventTasklet(object methname) {
    object meth = GetSelfAttr(methname);
    return mLevel->GetActorTasker()->CreateTasklet(meth);
}

object AActor::CreateEventTasklet(const char* methname) {
    object meth = GetSelfAttr(methname);
    return mLevel->GetActorTasker()->CreateTasklet(meth);
}

void AActor::SetOwner(const shared_ptr<AActor>& other) {
    if (mOwner) {
        mOwner->OnLostChild(GetSelfPtr<AActor>());
    }
    mOwner = other;
    if (mOwner) {
        mOwner->OnGainedChild(GetSelfPtr<AActor>());
    }
    mNetDirty = true;
}

void AActor::_OnGainedChild(shared_ptr<AActor> other) {
    mChildren.push_back(other);
}

void AActor::_OnLostChild(shared_ptr<AActor> other) {
    mChildren.erase(std::find(mChildren.begin(), mChildren.end(), other));
}

// Replica3 Implementation

void AActor::WriteAllocationID(rn::Connection_RM3* dest, rn::BitStream* out) const {
    // sint32 - levelNameHash
    // string - className
    // string - actorName
    // bool - hasOwner
    // sint32 - ownerNameHash

    object selfClass = GetSelfAttr("__class__");
    str className = str(".").join(make_tuple(selfClass.attr("__module__"),
                                             selfClass.attr("__name__")));
    sint32 levelNameHash = mLevel->GetNameHashAsInt();
    str actorName = extract<str>(mName);

    out->Write(levelNameHash);
    out->Write(PyString_AS_STRING(className.ptr()));
    out->Write(PyString_AS_STRING(actorName.ptr()));
    if (mOwner) {
        sint32 ownerNameHash = mOwner->GetNameHashAsInt();
        out->Write((bool)true);
        out->Write(ownerNameHash);
    }
    else {
        out->Write((bool)false);
    }
}

rn::RM3ConstructionState AActor::QueryConstruction(rn::Connection_RM3* dest, rn::ReplicaManager3* rm) {
    if (mRemoteRole != ROLE_NONE) {
        return QueryConstruction_ServerConstruction(
                   dest, !mLevel->GetWorld()->GetNetIsClient());
    }
    else {
        return rn::RM3CS_NO_ACTION;
    }
}

rn::RM3DestructionState AActor::QueryDestruction(rn::Connection_RM3 *dest, rn::ReplicaManager3 *rm) {
    //if (mLevel->GetWorld()->GetNetIsServer() &&
    //    mRole == ROLE_AUTHORITY && mRemoteRole == ROLE_NONE) {
    //    return rn::RM3DS_SEND_DESTRUCTION;    
    //}
    //else {
    //    return rn::RM3DS_NO_ACTION;
    //}

    // Destruction is handled expicitly by setting RemoteRole to ROLE_NONE
    return rn::RM3DS_NO_ACTION;
}

bool AActor::QueryRemoteConstruction(rn::Connection_RM3* source) {
    return QueryRemoteConstruction_ServerConstruction(
               source, !mLevel->GetWorld()->GetNetIsClient());
}

// Returns true if the top owner of actor is the player controller
// for the specified connection.
static bool CheckNetOwner(const AActor& actor, rn::Connection_RM3* connb) {
    //FRMConnection* conn = static_cast<FRMConnection*>(connb);
    //assert(conn->GetPlayer());
    //if (conn->GetPlayer()->GetController()) {
    //    shared_ptr<AActor> topown = actor.GetTopOwner();
    //    if (topown == conn->GetPlayer()->GetController()) {
    //        return true;
    //    }
    //}
    return false;
}

void AActor::SerializeConstruction(rn::BitStream* bs, rn::Connection_RM3* dest) {
    EnsureReplicator();
    mNetOwner = CheckNetOwner(*this, dest);
    mReplicator->ReplicateNative(bs, true);
    mNetOwner = false;
}

bool AActor::DeserializeConstruction(rn::BitStream* bs, rn::Connection_RM3* source) {
    EnsureReplicator();
    mReplicator->UpdateNative(bs);
    return true;
}

void AActor::SerializeDestruction(rn::BitStream* bs, rn::Connection_RM3* dest) {
    EnsureReplicator();
    mNetOwner = CheckNetOwner(*this, dest);
    mReplicator->ReplicateNative(bs, false);
    mNetOwner = false;
}

bool AActor::DeserializeDestruction(rn::BitStream* bs, rn::Connection_RM3* source) {
    EnsureReplicator();
    mReplicator->UpdateNative(bs);
    return true;
}

rn::RM3ActionOnPopConnection AActor::QueryActionOnPopConnection(RakNet::Connection_RM3 *dropped) const {
    //if (mRole == ROLE_AUTHORITY) {
    //    FRMConnection* conn = static_cast<FRMConnection*>(dropped);
    //    // If this actor is owned by the connection we just lost, destroy it
    //    if (conn->GetPlayer()->GetController() == GetTopOwner())
    //        return rn::RM3AOPC_DELETE_REPLICA_AND_BROADCAST_DESTRUCTION;
    //}
    return rn::RM3AOPC_DO_NOTHING;
}

rn::RM3QuerySerializationResult AActor::QuerySerialization(RakNet::Connection_RM3 *dest) {
    // Both clients replicate to server
    // and server replicates to clients
    if (mRemoteRole == ROLE_NONE) {
        return rn::RM3QSR_DO_NOT_CALL_SERIALIZE;
    }
    else {
        return rn::RM3QSR_CALL_SERIALIZE;
    }
}

rn::RM3SerializationResult AActor::Serialize(rn::SerializeParameters* sp) {
    EnsureReplicator();
    //assert(sp->pro[0].reliability == RELIABLE_SEQUENCED);
    sp->pro[0].reliability = RELIABLE_SEQUENCED;
    mNetOwner = CheckNetOwner(*this, sp->destinationConnection);
    if (mReplicator->ReplicateNative(&sp->outputBitstream[0], false)) {
        mNetOwner = false;
        return rn::RM3SR_SERIALIZED_ALWAYS;
    }
    else {
        mNetOwner = false;
        return rn::RM3SR_DO_NOT_SERIALIZE;
    }
}

void AActor::Deserialize(rn::DeserializeParameters* dp) {
    EnsureReplicator();
    mReplicator->UpdateNative(dp->serializationBitstream + 0);
}

void AActor::DeallocReplica(rn::Connection_RM3* source) {
    //if (!mLevel) {
    //    str name(GetSelf());
    //    format msg("DeallocReplica(): failed, no level for %1%");
    //    msg % PyString_AS_STRING(name.ptr());
    //    throw RuntimeError(msg);
    //}
    //else
    //    mLevel->DestroyActor(GetSelfPtr<AActor>());
}

//void AActor::BroadcastDestruction() {
//    if (mLevel->GetWorld()->GetNetIsEnabled())
//        rn::Replica3::BroadcastDestruction();
//}

PyObject* AActor::_GetAttr(PyObject* self, PyObject* name) {
    // Call the GetAttr we overwrote
    PyObject* result = msBaseGetAttr(self, name);
    // We only care about method objects that wrap Python functions (non-native)
    if (!result || !PyMethod_Check(result) || !PyFunction_Check(PyMethod_GET_FUNCTION(result)))
        return result;
    // Faster way of getting AActor* than using extract
    objects::instance<>* inst = reinterpret_cast<objects::instance<>*>(self);
    if (!inst->objects)
        return result; // return if no instance holder created yet
    // TODO: Implement a fast pointer getter for AActor or CObject.
    //       This would accesses the pointer directly rather than going
    //       through holds(). This may be dangerous, but might be worth it.
    AActor* actor = static_cast<AActor*>(inst->objects->holds(type_id<AActor>(), false));
    assert(actor);
    if (actor->mRole == ROLE_SIM_PROXY) {
        // Check if the function is simulated using direct get from dict with
        // pre-allocated string
        PyObject* dict = ((PyFunctionObject*)PyMethod_GET_FUNCTION(result))->func_dict;
        PyObject* simulated = NULL;
        if (dict)
            simulated = PyDict_GetItem(dict, msSimulatedStr.ptr());
        if (!simulated || simulated == Py_False)
            return incref(msSimulatedNoOpFunc.ptr());
        if (simulated == Py_True)
            return result;
        PyErr_SetString(PyExc_RuntimeError, "Invalid value for function.Simulated");
        return NULL;
    }
    return result;
}

int AActor::_SetAttr(PyObject* self, PyObject* name, PyObject* value) {
    int result = msBaseSetAttr(self, name, value);
    // TODO: Measure extract time
    if (result == 0) {
        objects::instance<>* instself = (objects::instance<>*)self;
        if (instself->objects) {
            AActor* actor = static_cast<AActor*>(instself->objects->holds(type_id<AActor>(), false));
            if (actor) {
                actor->SetNetDirty(true);
            }
        }
    }
    return result;
}

KH_BEGIN_GC(AActor)
    KH_GC_SHARED_PTR(thisx->mOwner)
    KH_GC_SHARED_PTR(thisx->mLevel)
    BOOST_FOREACH(shared_ptr<AActor>& i, thisx->mAttached) {
        KH_GC_SHARED_PTR(i)
    }
    KH_GC_SHARED_PTR(thisx->mAttachedTo)
    BOOST_FOREACH(shared_ptr<AActor>& i, thisx->mChildren) {
        KH_GC_SHARED_PTR(i)
    }
    KH_GC_SHARED_PTR(thisx->mReplicator)
KH_END_GC()

// ----
// APawn
// ----

//APawn::APawn(PyObject* self):
//    AActor(self) {}
//
//APawn::~APawn() {}
//
//template <typename T>
//static void ClampToMax(T& value, const T& maxvalue) {
//    if (value > maxvalue)
//        value = maxvalue;
//    else if (abs(value) > maxvalue)
//        value = maxvalue * -1;
//}
//
//void APawn::Tick(float deltaTime) {
//    {
//        FVector3F vel = GetVelocity();
//        FVector3F accel = GetAcceleration();
//        ClampToMax(vel.X, mMaxVelocity.X);
//        ClampToMax(vel.Y, mMaxVelocity.Y);
//        ClampToMax(vel.Z, mMaxVelocity.Z);
//        ClampToMax(accel.X, mMaxAcceleration.X);
//        ClampToMax(accel.Y, mMaxAcceleration.Y);
//        ClampToMax(accel.Z, mMaxAcceleration.Z);
//        SetVelocity(vel);
//        SetAcceleration(accel);
//    }
//    AActor::Tick(deltaTime);
//}
//
//void APawn::Draw(CEngine& engine) {
//    // Nothing yet
//    AActor::Draw(engine);
//}
//
//KH_BEGIN_GC(APawn)
//    KH_GC_SHARED_PTR(thisx->mController)
//KH_END_GC()

// ----
// AInfo
// ----

//AInfo::AInfo(PyObject* self):
//    AActor(self) {}
//
//
//KH_BEGIN_GC(AInfo)
//KH_END_GC()

// ----
// ALevelInfo
// ----

//ALevelInfo::ALevelInfo(PyObject* self):
//    AInfo(self) {}
//
//KH_BEGIN_GC(ALevelInfo)
//KH_END_GC()

// ----
// CActorReplicator
// ----

inline const FFieldInfo* GetFieldInfo(const FFieldInfo* fields, const char* name) {
    for (uint16 i = 0; fields[i].Type != FT_SENTRY; i++) {
        if (strcmp(name, fields[i].Name) == 0) {
            return &(fields[i]);
        }
    }
    return NULL;
}

static const FFieldInfo* GetFieldInfo(const FFieldInfo* fields, uint16 index, uint16* opt_offset=NULL) {
    for (int i = 0; fields[i].Type != FT_SENTRY; i++) {
        if (index == fields[i].Index) {
            if (opt_offset)
                *opt_offset = 0;
            return fields + i;
        }
        else if (index < fields[i].Index + fields[i].ArraySize) {
            if (opt_offset)
                *opt_offset = index - fields[i].Index;
            return fields + i;
        }
    }
    return NULL;
}

CActorReplicator::CActorReplicator(PyObject* self):
    CObject(self),
    mActor(),
    mActorClass(),
    mRoleInfo(NULL),
    mRemoteRoleInfo(NULL) {}

void CActorReplicator::BindActor(shared_ptr<AActor> actor) {
    call_method<void>(mSelf, "BindActor", actor);
}

void CActorReplicator::_BindActor(shared_ptr<AActor> actor) {
    if (!actor)
        throw NullPointerError("actor");
    if (!mHasNativeReplication)
        return;
    assert(mActorClass);
    mFullFieldInfo.clear();
    //actor->GetFullFieldInfo(mFullFieldInfo);
    assert(mFullFieldInfo.size() > 0);

    // Add in the sentry
    FFieldInfo sentry = {FT_SENTRY};
    mFullFieldInfo.push_back(sentry);
    FFieldInfo* fieldInfo = &mFullFieldInfo.front();

    // Initialize with storage offsets and indicies
    for (int i = 0; fieldInfo[i].Type != FT_SENTRY;) {
        fieldInfo[i].Index = i;
        if (fieldInfo[i].ArraySize < 1) {
            i++;
        }
        else {
            i += fieldInfo[i].ArraySize;
        }
    }

    // Find the size of the largest class and number of indicies
    uint16 size = 0;
    uint16 storageSize = 0;
    for (uint16 i = 0; fieldInfo[i].Type != FT_SENTRY; i++) {
        if (fieldInfo[i].ClassSize > storageSize)
            storageSize = fieldInfo[i].ClassSize;
        size++;
    }

    // Allocate storage for previously sent values and indicies
    mRecentNative.resize(storageSize);
    mIndices.resize(size * 2, 0);
    // Special case Role and RemoteRole
    mRoleInfo = GetFieldInfo(fieldInfo, "mRole");
    mRemoteRoleInfo = GetFieldInfo(fieldInfo, "mRemoteRole");
    assert(mRoleInfo && mRemoteRoleInfo);
    mActor = actor;
}

void CActorReplicator::UnbindActor() {
    mFullFieldInfo.clear();
    mRoleInfo = NULL;
    mRemoteRoleInfo = NULL;
    mRecentNative.clear();
    mIndices.clear();
    mActor.reset();
    mActorClass = object();
    mInitial = false;

    mNetVarsList = make_tuple();
    mNetVarsDict = dict();
    mRecentPython = list();
}

// NATIVE REPLICATION TESTING

static const FFieldInfo* GetFieldInfoRep(const FFieldInfo* fields, const char* name) {
    const FFieldInfo* info = GetFieldInfo(fields, name);
    if (!info) {
        throw RuntimeError(format("GetNativeRepVars(): Undefined field %1%") % name);
    }
    return info;
}

#define KH_DOREP(typename_, varname) {\
    if (reinterpret_cast<const typename_*>(recent)->varname != this->varname) { \
        static const FFieldInfo* info = GetFieldInfoRep(fields, #varname); \
        indices[0] = info->Index; \
        indices[1] = 0; \
        indices+=2; \
    } \
}

#define KH_DOREP_ARRAY(typename_, varname) {\
    { \
        static const FFieldInfo* info = GetFieldInfoRep(fields, #varname); \
        static const uint16 fas = info->ArraySize; \
        for (int i = 0; i < fas; i++) { \
            if (reinterpret_cast<const typename_*>(recent)->varname[i] != this->varname[i]) { \
                indices[0] = info->Index; \
                indices[1] = i; \
                indices+=2; \
            } \
        } \
    } \
}

sint16* AActor::GetNativeRepVars(sint16* indices, const uint8* recent, const FFieldInfo* fields) {
    // To Client
    if (mNetDirty && mRole == ROLE_AUTHORITY && !mNetSkipActorReplication) {
        KH_DOREP(AActor, mHidden)
        KH_DOREP(AActor, mRole)
        KH_DOREP(AActor, mRemoteRole)
        KH_DOREP(AActor, mBounds)
        KH_DOREP(AActor, mCollideActors)
        KH_DOREP(AActor, mCollideTiles)
        KH_DOREP(AActor, mCollisionLayer)
        if (mRemoteRole == ROLE_SIM_PROXY) {
            KH_DOREP(AActor, mDirection)
            KH_DOREP(AActor, mPosition)
            KH_DOREP(AActor, mVelocity)
            KH_DOREP(AActor, mAcceleration)
        }
    }
    return indices;
}

//sint16* ACamera::GetNativeRepVars(sint16* indices, const uint8* recent, const FFieldInfo* fields) {
//    indices = AActor::GetNativeRepVars(indices, recent, fields);
//    // To Client
//    if (mNetDirty) {
//        if (mRole == ROLE_AUTHORITY) {
//            KH_DOREP(ACamera, mTarget)
//            KH_DOREP(ACamera, mTargetOffset)
//        }
//    }
//    return indices;
//}

//sint16* APawn::GetNativeRepVars(sint16* indices, const uint8* recent, const FFieldInfo* fields) {
//    indices = AActor::GetNativeRepVars(indices, recent, fields);
//    if (mNetDirty) {
//        if (mRole == ROLE_AUTHORITY) {
//            KH_DOREP(APawn, mMaxVelocity)
//            KH_DOREP(APawn, mMaxAcceleration)
//            if (mNetOwner) {
//                KH_DOREP(APawn, mController)
//            }
//        }
//    }
//    return indices; 
//}

bool CActorReplicator::ReplicateNative(rn::BitStream* out, bool initial) {
    //const uint16 MAX_NAME_LENGTH = 0xFF;

    //if (!mActor)
    //    throw RuntimeError("no actor bound");

    //assert(mFullFieldInfo.size() > 0);
    //const FFieldInfo* fields = &mFullFieldInfo.front();
    //const byte* recent = &mRecentNative.front();
    //mIndices.assign(mIndices.size(), -1);

    //if (initial) {
    //    mActor->mNetInitial = true;
    //    mActor->mNetDirty = true;
    //}
    //mActor->GetNativeRepVars(&mIndices.front(), recent, fields);

    //object prstr = ReplicatePython();
    //if (!prstr.is_none() && !PyString_CheckExact(prstr.ptr())) {
    //    throw RuntimeError("python replication string is wrong type");
    //}

    //mActor->mNetDirty = false;
    //mActor->mNetInitial = false;

    //uint8 written = 0;
    //for (int i = 0; mIndices[i] != -1; i+=2) {
    //    const FFieldInfo* info = fields + mIndices[i];
    //    uint16 offset = mIndices[i+1];
    //    uint16 offsetBytes = offset * info->Size;

    //    assert(info->Index <= FI_LIMIT);

    //    byte* valuePtr = ((byte*)mActor.get()) + info->Offset + offsetBytes;
    //    memcpy((void*)(recent + info->Offset + offsetBytes), valuePtr, info->Size);

    //    // Copy the 2-byte index and special case Role/RemoteRole
    //    if (info == mRoleInfo)
    //        out->Write(mRemoteRoleInfo->Index);
    //    else if (info == mRemoteRoleInfo)
    //        out->Write(mRoleInfo->Index);
    //    else
    //        out->Write<uint16>(info->Index + offset);

    //    switch (info->Type) {
    //    // Write actor name to stream
    //    case FT_SHARED_PTR_ACTOR:
    //        // Write the referenced actor's name
    //        {
    //            AActor* actor = ((shared_ptr<AActor>*)valuePtr)->get();
    //            if (!actor ||
    //                actor->GetRemoteRole() == ROLE_NONE ||
    //                actor->GetLevel() != mActor->GetLevel())
    //            {
    //                out->Write(false);
    //            }
    //            else {
    //                // Send the name hash
    //                sint32 nameHash = actor->GetNameHashAsInt();
    //                out->Write(true);
    //                out->Write(nameHash);
    //            }
    //        }
    //        break;
    //    case FT_VECTOR3F:
    //        // Compress vector
    //        {
    //            FVector3F* vec = (FVector3F*)valuePtr;
    //            out->WriteVector(vec->X, vec->Y, vec->Z);
    //        }
    //        break;

    //    default:
    //        // Default copy by value
    //        out->Write((const char*)valuePtr, info->Size);
    //        break;
    //    }
    //    written++;
    //}
    //// Write python pickle
    //if (!prstr.is_none()) {
    //    out->Write<uint16>(FI_PYTHON);
    //    out->Write<uint16>(PyString_GET_SIZE(prstr.ptr()));
    //    out->Write(PyString_AS_STRING(prstr.ptr()),
    //               PyString_GET_SIZE(prstr.ptr()));
    //    written++;
    //}
    //// mark end of indicies
    //if (written > 0) {
    //    out->Write<uint16>(FI_END);
    //    return true;
    //}
    //return false;
    return false;
}

// Python attribute replication function, ported from Python
object CActorReplicator::ReplicatePython() {
    const int ITERATION_LIMIT = 255;
    object mro, rlci, idx, gen, nextfunc, reptuple;
    list reptuples, changed;

    rlci = mActorClass.attr("_RepListClassIndices");
    mro = mActorClass.attr("mro")();
    for (int i = 0; i < len(rlci); i++) {
        idx = rlci[i];

        gen = mro[idx].attr("GetRepList")(mActor);
        if (!PyGenerator_Check(gen.ptr())) {
            throw TypeError("GetRepList must return a generator");
        }
        nextfunc = gen.attr("next");
        for (int j = 0; i <= ITERATION_LIMIT && core::Iterate(nextfunc, reptuple); j++) {
            reptuples.append(reptuple);
        }
    }

    object nvindex, nvname, nvvalue, repvars, actorobj(mActor);
    for (int i = 0; i < len(reptuples); i++) {
        repvars = reptuples[i];
        for (int j = 0; j < len(repvars); j++) {
            nvname = repvars[j];
            try {
                nvindex = mNetVarsDict[nvname];
            }
            catch (error_already_set&) {
                if (PyErr_ExceptionMatches(PyExc_KeyError)) {
                    PyErr_Clear(); // necessary?
                    PyErr_SetString(PyExc_RuntimeError, "invalid replication variable name");
                }
                throw;
            }
            nvvalue = actorobj.attr(nvname);
            if (nvvalue != mRecentPython[nvindex]) {
                changed.append(nvindex);
                changed.append(nvvalue);
                mRecentPython[nvindex] = nvvalue;
            }
        }
    }

    if (len(changed) > 0) {
        return mPickleFunc(changed, mPickleProtocol);
    }
    return object();
}

bool CActorReplicator::UpdateNative(rn::BitStream* in) {
    //const int LIMIT = 255;
    //if (!mActor)
    //    throw RuntimeError("no actor bound");
    //uint16 index;
    //
    //assert(mFullFieldInfo.size() > 0);
    //const FFieldInfo* fieldInfo = &mFullFieldInfo.front();

    //bool read = in->Read(index);
    //if (!read)
    //    return false;
    //assert(index != FI_END);
    //int i = 0;
    //while (index != FI_END && i <= LIMIT && read) {
    //    if (index == FI_PYTHON) {
    //        // Unpack the pickled python data
    //        uint16 sz;
    //        in->Read(sz);
    //        scoped_array<char> datastr(new char[sz]);
    //        in->Read(datastr.get(), sz);
    //        UpdatePython(str(datastr.get(), sz));
    //        mActor->SetNetDirty(false);
    //    }
    //    else {
    //        uint16 offset;
    //        const FFieldInfo* info = GetFieldInfo(fieldInfo, index, &offset);
    //        if (!info) {
    //            str act(mActor);
    //            format msg("UpdateNative(): unknown field %1% while updating %2%");
    //            mActor->GetLevel()->GetLogger()->Error(msg % index % PyString_AS_STRING(act.ptr()));
    //            //return false;
    //            goto next_read;
    //        }
    //        uint16 offsetBytes = offset * info->Size;
    //        byte* valuePtr = ((byte*)mActor.get()) + info->Offset + offsetBytes;
    //        switch (info->Type) {
    //        case FT_SHARED_PTR_ACTOR:
    //            {
    //                bool specified;
    //                in->Read(specified);
    //                if (specified) {
    //                    sint32 nameh;
    //                    in->Read(nameh);
    //                    shared_ptr<AActor> nactor = mActor->GetLevel()->GetActor(object(nameh));
    //                    if (!nactor) {
    //                        format msg("received reference to non-existent actor: %1%");
    //                        msg % nameh;
    //                        throw RuntimeError(msg);
    //                    }
    //                    *((shared_ptr<AActor>*)valuePtr) = nactor;
    //                }
    //                else {
    //                    ((shared_ptr<AActor>*)valuePtr)->reset();
    //                }
    //            }
    //            break;
    //        case FT_VECTOR3F:
    //            {
    //                FVector3F* vec = (FVector3F*)valuePtr;
    //                in->ReadVector(vec->X, vec->Y, vec->Z);
    //            }
    //            break;
    //        default:
    //            in->Read((char*)valuePtr, info->Size);
    //            break;
    //        }
    //    }
    //    next_read:
    //    read = in->Read(index);
    //    i++;
    //}
    //return true;
    return false;
}

KH_BEGIN_GC(CActorReplicator)
    KH_GC_SHARED_PTR(thisx->mActor)
    KH_GC_OBJECT(thisx->mActorClass)
KH_END_GC()

*/

struct FMemberWrapper {
    const std::type_info& ClassType;
    const std::type_info& DataType;

    FMemberWrapper(const std::type_info& ct, const std::type_info& dt):
        ClassType(ct),
        DataType(dt) {}

    virtual void* Get(void* obj) const = 0;
    virtual void Set(void* obj, void* value) const = 0;
};

template <class T, class D>
struct FMemberWrapperImpl: public FMemberWrapper {
    D T::* MemberPtr;

    FMemberWrapperImpl(D T::* mp):
        FMemberWrapper(typeid(T), typeid(D)),
        MemberPtr(mp) {}

    virtual void* Get(void* obj) const {
        return (void*)&(((T*)obj)->*MemberPtr);
    }
    
    virtual void Set(void* obj, void* value) const {
        (*((T*)obj)).*MemberPtr = *((D*)value);
    }
};

template <class T, class D>
shared_ptr<FMemberWrapper> MakeNewWrapper(D T::* mp) {
    return shared_ptr<FMemberWrapper>(new FMemberWrapperImpl<T, D>(mp));
}

void _MemberTest(shared_ptr<ATransform> t) {
    shared_ptr<FMemberWrapper> mg = MakeNewWrapper(&ATransform::mPosition);

    assert(typeid(ATransform) == mg->ClassType);
    assert(typeid(FVector3F) == mg->DataType);

    FVector3F* pos = (FVector3F*)mg->Get(t.get());

    assert(*pos == t->mPosition);
    
    FVector3F npos(3.5f, 6.11f, 0.0f);
    mg->Set(t.get(), &npos);

    assert(npos == t->mPosition);
}

void _InitActorClasses() {
    def("MemberTest", &_MemberTest);

    _InitActorBindings();

#define INIT_SLOT(TYPE) \
    { \
        object cls = GetTypeObject(type_id<A##TYPE>()); \
        CActor::msComponentSlotTypes[SLOT_##TYPE] = \
            handle<PyTypeObject>(borrowed((PyTypeObject*)cls.ptr())); \
    }

    INIT_SLOT(Transform)
    INIT_SLOT(Movement)
    INIT_SLOT(Render)
    INIT_SLOT(Physics)
    INIT_SLOT(Camera)
    INIT_SLOT(Pawn)
    INIT_SLOT(PlayerController)

    // END NEW STUFF
    /*
    object actorclass = class_
        <AActor, shared_ptr<AActor>, bases<core::CObject>, boost::noncopyable>
        ("AActorBase", init<>())
        .def_readonly("Name", &AActor::mName)
        .def_readonly("Level", &AActor::mLevel)
        .add_property("Owner", make_getter(&AActor::mOwner), &AActor::SetOwner)
        .def_readonly("Static", &AActor::mStatic)
        .def_readonly("NoDelete", &AActor::mNoDelete)
        .def_readonly("DeleteMe", &AActor::mDeleteMe)
        .add_property("NetInitial", &AActor::GetNetInitial, &AActor::SetNetInitial)
        .add_property("NetDirty", &AActor::GetNetDirty, &AActor::SetNetDirty)
        .add_property("NetSkipActorReplication", &AActor::GetNetSkipActorReplication, &AActor::SetNetSkipActorReplication)
        .add_property("Direction", &AActor::GetDirection, &AActor::SetDirection)
        .add_property("Hidden", &AActor::GetHidden, &AActor::SetHidden)
        .add_property("Position", make_getter(&AActor::mPosition), &AActor::SetPosition)
        .add_property("Velocity", make_getter(&AActor::mVelocity), &AActor::SetVelocity)
        .add_property("Acceleration", make_getter(&AActor::mAcceleration), &AActor::SetAcceleration)
        .add_property("Bounds", make_getter(&AActor::mBounds), &AActor::SetBounds)
        .add_property("CollideActors", &AActor::GetCollideActors, &AActor::SetCollideActors)
        .add_property("CollideTiles", &AActor::GetCollideTiles, &AActor::SetCollideTiles)
        .add_property("CollisionLayer", &AActor::GetCollisionLayer, &AActor::SetCollisionLayer)
        .add_property("Role", &AActor::GetRole, &AActor::SetRole)
        .add_property("RemoteRole", &AActor::GetRemoteRole, &AActor::SetRemoteRole)
        .def("Move", (void (AActor::*)(const FVector3F&))&AActor::Move)
        .def("_Sleep", &AActor::Sleep, (arg("time"), arg("group")=core::CTasker::CURRENT_GROUP))
        .def("_Yield", &AActor::Yield, (arg("group")=core::CTasker::CURRENT_GROUP))
        .def("OnCreated", &AActor::_OnCreated)
        .def("OnDestroyed", &AActor::_OnDestroyed)
        .def("OnTouched", &AActor::_OnTouched)
        .def("OnGainedChild", &AActor::_OnGainedChild)
        .def("OnLostChild", &AActor::_OnLostChild)
        .def("__nonzero__", &AActor::IsValid)
        .enable_pickling()
        ;

    KH_IMPL_GC(AActor)

    // Special Actor initialization
    {
        AActor::msSimulatedNoOpFunc = GInnerModule.attr("_SimulatedNoOp");
        assert(PyFunction_Check(AActor::msSimulatedNoOpFunc.ptr()));
        AActor::msSimulatedStr = str("Simulated"); // so one less thing to do

        // Inject custom attribute getter and setter into actor class.
        //
        // We have to do all this work, because defining these in the Boost.Python
        // class would result in more work to do as Python goes through the steps
        // of translating its call into a C++ function call through Boost.Python.
        // 
        // Instead, we insert the getter and setter functions into the class
        // after it has been created, which forces us to update the type slot
        // wrappers to reflect the new functions.

        PyTypeObject* ato = reinterpret_cast<PyTypeObject*>(actorclass.ptr());
        AActor::msBaseGetAttr = ato->tp_getattro;
        AActor::msBaseSetAttr = ato->tp_setattro;
        ato->tp_getattro = &AActor::_GetAttr;
        ato->tp_setattro = &AActor::_SetAttr;
        // Update the existing wrapper so it does not revert to the previously
        // specified tp_getattro
        const char* fail = NULL;
        {
            // PYTHON C API CODE
            PyObject *gastr = NULL, *sastr = NULL;
            PyWrapperDescrObject *bgd = NULL, *bsd = NULL,
                                 *ngd = NULL, *nsd = NULL;

            // Create strings
            gastr = PyString_InternFromString("__getattribute__");
            sastr = PyString_InternFromString("__setattr__");
            if (!gastr || !sastr) {
                fail = "no memory for strings";
                goto cleanup;
            }

            // Get base descriptor (should never fail)
            bgd = (PyWrapperDescrObject*)_PyType_Lookup(ato, gastr);
            bsd = (PyWrapperDescrObject*)_PyType_Lookup(ato, sastr);
            if (!bgd || !bsd) {
                fail = "no base wrapper descriptor";
                goto cleanup;
            }

            // Type check
            if (Py_TYPE(bgd) != &PyWrapperDescr_Type ||
                Py_TYPE(bsd) != &PyWrapperDescr_Type) {
                fail = "base __getattribute__ or __setattr__ not a wrapper descriptor";
                goto cleanup;
            }

            assert(Py_REFCNT(bgd) == 1 && Py_REFCNT(bsd) == 1);

            // Create new wrapper descriptors based on old ones
            ngd = (PyWrapperDescrObject*)PyDescr_NewWrapper(ato, bgd->d_base, &AActor::_GetAttr);
            if (!ngd) {
                fail = "failed to create new __getattribute__ descr";
                goto cleanup;
            }
            if (PyDict_SetItem(ato->tp_dict, gastr, (PyObject*)ngd) < 0) {
                fail = "failed to insert new __getattribute__ into type dict";
                goto cleanup;
            }
 
            nsd = (PyWrapperDescrObject*)PyDescr_NewWrapper(ato, bsd->d_base, &AActor::_SetAttr);
            if (!nsd) {
                fail = "failed to create new __setattr__ descr";
                goto cleanup;
            }
        
            if (PyDict_SetItem(ato->tp_dict, sastr, (PyObject*)nsd) < 0) {
                fail = "failed to insert new __setattr__ into type dict";
                goto cleanup;
            }
            // Need to insert it for __delattr__ too, otherwise the slot will
            // be overwritten with the generic function, and cause another error
            if (PyDict_SetItemString(ato->tp_dict, "__delattr__", (PyObject*)nsd) < 0) {
                fail = "failed to insert new __delattr__ into type dict";
                goto cleanup;
            }


        cleanup:
            Py_XDECREF(ngd);
            Py_XDECREF(nsd);
            Py_XDECREF(gastr);
            Py_XDECREF(sastr);
        }
        if (fail) {
            format msg("Failed to insert Actor __getattribute__ and __setattr__: %1%");
            msg % fail;
            throw RuntimeError(msg);
        }
    }

    class_<CActorReplicator, shared_ptr<CActorReplicator>, bases<core::CObject>, boost::noncopyable>
        ("CActorReplicatorBase", init<>())
        .def_readonly("Actor", &CActorReplicator::mActor)
        .def_readwrite("ActorClass", &CActorReplicator::mActorClass)
        .def_readwrite("HasNativeReplication", &CActorReplicator::mHasNativeReplication)
        .def_readwrite("NetVarsList", &CActorReplicator::mNetVarsList)
        .def_readwrite("NetVarsDict", &CActorReplicator::mNetVarsDict)
        .def_readwrite("RecentPython", &CActorReplicator::mRecentPython)
        .def_readwrite("PickleFunc", &CActorReplicator::mPickleFunc)
        .def_readwrite("PickleProtocol", &CActorReplicator::mPickleProtocol)
        .def("ReplicateNative", &CActorReplicator::ReplicateNative)
        .def("ReplicatePython", &CActorReplicator::ReplicatePython)
        .def("BindActor", &CActorReplicator::_BindActor)
        .def("UnbindActor", &CActorReplicator::UnbindActor)
        ;

    KH_IMPL_GC(CActorReplicator)
    */
}

void _ExitActorClasses() {
    //AActor::msBaseGetAttr = NULL;
    //AActor::msBaseSetAttr = NULL;
    //AActor::msSimulatedNoOpFunc = object();
    //AActor::msSimulatedNoOpMeth = object();
    //AActor::msSimulatedStr = object();
    for (int i = 0; i < SLOT_LAST; i++) {
        CActor::msComponentSlotTypes[i].reset();
    }
}

}}